﻿using System;

using System.IO;
using RoughJs.Helper;

namespace RoughJs.Ast
{
    /// <summary>
    /// 2010-01-5 ----- 2010-01-29
    /// 陈曦 1.0
    /// Microsoft Public License (Ms-PL)
    ///This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    ///1. Definitions
    ///The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
    ///A "contribution" is the original software, or any additions or changes to the software.
    ///A "contributor" is any person that distributes its contribution under this license.
    ///"Licensed patents" are a contributor's patent claims that read directly on its contribution.
    ///2. Grant of Rights
    ///(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    ///(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    ///3. Conditions and Limitations
    ///(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
    ///(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    ///(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    ///(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    ///(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.  
    /// 
    /// 
    /// 得到标记流
    /// </summary>
    public class TokenStream
    {
        public TokenStream(Parser parser, StreamReader sourceReader,
            String sourceString, int lineno)
        {
            this.parser = parser;
            this.lineno = lineno;
            if (sourceReader != null)
            {
                this.sourceString = sourceReader.ReadToEnd();
                this.sourceEnd = this.sourceString.Length;
                sourceReader.Close();
            }
            else
            {
                this.sourceString = sourceString;
                this.sourceEnd = sourceString.Length;
            }
            this.sourceCursor = 0;
        }

        /// <summary>
        /// 得到文件的行号
        /// </summary>
        /// <returns></returns>
        public int getLineno() { return lineno; }
        public bool eof() { return hitEOF; }
        /// <summary>
        /// 得到字符数据
        /// </summary>
        /// <returns></returns>
        public String getString() { return _string; }

        public double getNumber() { return _number; }

        public int getToken()
        {
            int c = 0;
        retry:
            for (; ; )
            {
                for (; ; )
                {
                    c = getChar();//取出一个字符
                    if (c == EOF_CHAR)//是否是结束符
                        return Token.EOF;
                    else if (c == '\n')
                    {//如果是换行
                        dirtyLine = false;
                        return Token.EOL;
                    }
                    else if (!isJSSpace(c))
                    {
                        if (c != '-')
                        {
                            dirtyLine = true;
                        }
                        break;
                    }

                }
                if (c == '@') return Token.XMLATTR;
                // identifier/keyword/instanceof?
                // watch out for starting with a <backslash>
                bool identifierStart;
                bool isUnicodeEscapeStart = false;
                if (c == '\\')
                {
                    c = getChar();
                    if (c == 'u')
                    {
                        identifierStart = true;
                        isUnicodeEscapeStart = true;
                        stringBufferTop = 0;
                    }
                    else
                    {
                        identifierStart = false;
                        ungetChar(c);
                        c = '\\';
                    }
                }
                else
                {
                    //   identifierStart = Character.isJavaIdentifierStart((char)c);
                    identifierStart = Character.isJavaIdentifierStart((char)c);
                    if (identifierStart)
                    {
                        stringBufferTop = 0;
                        addToString(c);
                    }
                }
                if (identifierStart)
                {
                    bool containsEscape = isUnicodeEscapeStart;
                    for (; ; )
                    {
                        if (isUnicodeEscapeStart)
                        {
                            int escapeVal = 0;
                            for (int i = 0; i != 4; ++i)
                            {
                                c = getChar();
                                escapeVal = Kit.xDigitToInt(c, escapeVal);
                                
                                if (escapeVal < 0) { break; }
                            }
                        }
                        else
                        {
                            c = getChar();
                            if (c == '\\')
                            {
                                c = getChar();
                                if (c == 'u')
                                {
                                    isUnicodeEscapeStart = true;
                                    containsEscape = true;
                                }
                                else
                                {
                                    parser.addError("msg.illegal.character");
                                    return Token.ERROR;
                                }
                            }
                            else
                            {
                                if (c == EOF_CHAR
                                    || !Character.isJavaIdentifierPart((char)c))
                                {
                                    break;
                                }
                                addToString(c);
                            }
                        }
                    }
                    ungetChar(c);
                    String str = getStringFromBuffer();
                    if (!containsEscape)
                    {
                        int result = stringToKeyword(str);
                        if (result != Token.EOF)
                        {
                            //if ((result == Token.LET || result == Token.YIELD)&& parser.compilerEnv.getLanguageVersion() 
                            //   < Context.VERSION_1_7)
                            //{
                            //    _string = result == Token.LET ? "let" : "yield";
                            //    result = Token.NAME;
                            //}

                            if (result != Token.RESERVED)
                            {
                                return result;
                            }
                            else if (!parser.compilerEnv.
                                        isReservedKeywordAsIdentifier())
                            {
                                return result;
                            }
                            else
                            {
                                parser.addWarning("msg.reserved.keyword", str);
                            }
                        }
                    }
                    this._string = (String)allStrings.intern(str);
                    return Token.NAME;
                }
                //是否是数字
                if (isDigit(c) || (c == '.' && isDigit(peekChar())))
                {

                    stringBufferTop = 0;
                    int base1 = 10;

                    if (c == '0')
                    {
                        c = getChar();
                        if (c == 'x' || c == 'X')
                        {
                            base1 = 16;
                            c = getChar();
                        }
                        else if (isDigit(c))
                        {
                            base1 = 8;
                        }
                        else
                        {
                            addToString('0');
                        }
                    }

                    if (base1 == 16)
                    {
                        while (0 <= Kit.xDigitToInt(c, 0))
                        {
                            addToString(c);
                            c = getChar();
                        }
                    }
                    else
                    {
                        while ('0' <= c && c <= '9')
                        {

                            if (base1 == 8 && c >= '8')
                            {
                                parser.addWarning("msg.bad.octal.literal",
                                                  c == '8' ? "8" : "9");
                                base1 = 10;
                            }
                            addToString(c);
                            c = getChar();
                        }
                    }

                    bool isInteger = true;

                    if (base1 == 10 && (c == '.' || c == 'e' || c == 'E'))
                    {
                        isInteger = false;
                        if (c == '.')
                        {
                            do
                            {
                                addToString(c);
                                c = getChar();
                            } while (isDigit(c));
                        }
                        if (c == 'e' || c == 'E')
                        {
                            addToString(c);
                            c = getChar();
                            if (c == '+' || c == '-')
                            {
                                addToString(c);
                                c = getChar();
                            }
                            if (!isDigit(c))
                            {
                                parser.addError("msg.missing.exponent");
                                return Token.ERROR;
                            }
                            do
                            {
                                addToString(c);
                                c = getChar();
                            } while (isDigit(c));
                        }
                    }
                    ungetChar(c);
                    String numString = getStringFromBuffer();

                    double dval;
                    if (base1 == 10 && !isInteger)
                    {
                        try
                        {
                            // Use Java conversion to number from string...
                            dval = Convert.ToDouble(numString);
                        }
                        catch (Exception ex)
                        {
                            parser.addError("msg.caught.nfe");
                            return Token.ERROR;
                        }
                    }
                    else
                    {
                        dval = ScriptRuntime.stringToNumber(numString, 0, base1);
                    }

                    this._number = dval;
                    return Token.NUMBER;
                }
                //是否是字符
                if (c == '"' || c == '\'')
                {
                    int quoteChar = c;
                    stringBufferTop = 0;

                    c = getChar();
                strLoop: while (c != quoteChar)
                    {
                        if (c == '\n' || c == EOF_CHAR)
                        {
                            ungetChar(c);
                            parser.addError("msg.unterminated.string.lit");
                            return Token.ERROR;
                        }

                        if (c == '\\')
                        {
                            // 转义符
                            int escapeVal;

                            c = getChar();
                            switch (c)
                            {
                                case 'b': c = '\b'; break;
                                case 'f': c = '\f'; break;
                                case 'n': c = '\n'; break;
                                case 'r': c = '\r'; break;
                                case 't': c = '\t'; break;

                                //ECMA空格
                                case 'v': c = 0xb; break;

                                case 'u':
                                    //4进制
                                    int escapeStart = stringBufferTop;
                                    addToString('u');
                                    escapeVal = 0;
                                    for (int i = 0; i != 4; ++i)
                                    {
                                        c = getChar();
                                        escapeVal = Kit.xDigitToInt(c, escapeVal);
                                        if (escapeVal < 0)
                                        {
                                            goto strLoop;
                                        }
                                        addToString(c);
                                    }
                                    //替换u
                                    stringBufferTop = escapeStart;
                                    c = escapeVal;
                                    break;
                                case 'x':
                                    //2进制数
                                    c = getChar();
                                    escapeVal = Kit.xDigitToInt(c, 0);
                                    if (escapeVal < 0)
                                    {
                                        addToString('x');
                                        goto strLoop;
                                    }
                                    else
                                    {
                                        int c1 = c;
                                        c = getChar();
                                        escapeVal = Kit.xDigitToInt(c, escapeVal);
                                        if (escapeVal < 0)
                                        {
                                            addToString('x');
                                            addToString(c1);
                                            goto strLoop;
                                        }
                                        else
                                        {
                                            // got 2 hex digits
                                            c = escapeVal;
                                        }
                                    }
                                    break;

                                case '\n':
                                    //移除行
                                    c = getChar();
                                    goto strLoop;

                                default:
                                    if ('0' <= c && c < '8')
                                    {
                                        int val = c - '0';
                                        c = getChar();
                                        if ('0' <= c && c < '8')
                                        {
                                            val = 8 * val + c - '0';
                                            c = getChar();
                                            if ('0' <= c && c < '8' && val <= 037)
                                            {
                                                val = 8 * val + c - '0';
                                                c = getChar();
                                            }
                                        }
                                        ungetChar(c);
                                        c = val;
                                    }
                                    break;
                            }
                        }
                        addToString(c);
                        c = getChar();
                    }

                    String str = getStringFromBuffer();
                    this._string = (String)allStrings.intern(str);
                    return Token.STRING;
                }

                switch (c)
                {
                    case ';': return Token.SEMI;
                    case '[': return Token.LB;
                    case ']': return Token.RB;
                    case '{': return Token.LC;
                    case '}': return Token.RC;
                    case '(': return Token.LP;
                    case ')': return Token.RP;
                    case ',': return Token.COMMA;
                    case '?': return Token.HOOK;
                    case ':':
                        if (matchChar(':'))
                        {
                            return Token.COLONCOLON;
                        }
                        else
                        {
                            return Token.COLON;
                        }
                    case '.':
                        if (matchChar('.'))
                        {
                            return Token.DOTDOT;
                        }
                        else if (matchChar('('))
                        {
                            return Token.DOTQUERY;
                        }
                        else
                        {
                            return Token.DOT;
                        }

                    case '|':
                        if (matchChar('|'))
                        {
                            return Token.OR;
                        }
                        else if (matchChar('='))
                        {
                            return Token.ASSIGN_BITOR;
                        }
                        else
                        {
                            return Token.BITOR;
                        }

                    case '^':
                        if (matchChar('='))
                        {
                            return Token.ASSIGN_BITXOR;
                        }
                        else
                        {
                            return Token.BITXOR;
                        }

                    case '&':
                        if (matchChar('&'))
                        {
                            return Token.AND;
                        }
                        else if (matchChar('='))
                        {
                            return Token.ASSIGN_BITAND;
                        }
                        else
                        {
                            return Token.BITAND;
                        }

                    case '=':
                        if (matchChar('='))
                        {
                            if (matchChar('='))
                                return Token.SHEQ;
                            else
                                return Token.EQ;
                        }
                        else
                        {
                            return Token.ASSIGN;
                        }

                    case '!':
                        if (matchChar('='))
                        {
                            if (matchChar('='))
                                return Token.SHNE;
                            else
                                return Token.NE;
                        }
                        else
                        {
                            return Token.NOT;
                        }

                    case '<':
                        //html开头
                        if (matchChar('!'))
                        {
                            if (matchChar('-'))
                            {
                                if (matchChar('-'))
                                {
                                    skipLine();
                                    goto retry;
                                }
                                ungetCharIgnoreLineEnd('-');
                            }
                            ungetCharIgnoreLineEnd('!');
                        }
                        if (matchChar('<'))
                        {
                            if (matchChar('='))
                            {
                                return Token.ASSIGN_LSH;
                            }
                            else
                            {
                                return Token.LSH;
                            }
                        }
                        else
                        {
                            if (matchChar('='))
                            {
                                return Token.LE;
                            }
                            else
                            {
                                return Token.LT;
                            }
                        }

                    case '>':
                        if (matchChar('>'))
                        {
                            if (matchChar('>'))
                            {
                                if (matchChar('='))
                                {
                                    return Token.ASSIGN_URSH;
                                }
                                else
                                {
                                    return Token.URSH;
                                }
                            }
                            else
                            {
                                if (matchChar('='))
                                {
                                    return Token.ASSIGN_RSH;
                                }
                                else
                                {
                                    return Token.RSH;
                                }
                            }
                        }
                        else
                        {
                            if (matchChar('='))
                            {
                                return Token.GE;
                            }
                            else
                            {
                                return Token.GT;
                            }
                        }

                    case '*':
                        if (matchChar('='))
                        {
                            return Token.ASSIGN_MUL;
                        }
                        else
                        {
                            return Token.MUL;
                        }

                    case '/':
                        //内容
                        if (matchChar('/'))
                        {
                            skipLine();
                            goto retry;
                        }
                        if (matchChar('*'))
                        {
                            bool lookForSlash = false;
                            for (; ; )
                            {
                                c = getChar();
                                if (c == EOF_CHAR)
                                {
                                    parser.addError("msg.unterminated.comment");
                                    return Token.ERROR;
                                }
                                else if (c == '*')
                                {
                                    lookForSlash = true;
                                }
                                else if (c == '/')
                                {
                                    if (lookForSlash)
                                    {
                                        goto retry;
                                    }
                                }
                                else
                                {
                                    lookForSlash = false;
                                }
                            }
                        }

                        if (matchChar('='))
                        {
                            return Token.ASSIGN_DIV;
                        }
                        else
                        {
                            return Token.DIV;
                        }

                    case '%':
                        if (matchChar('='))
                        {
                            return Token.ASSIGN_MOD;
                        }
                        else
                        {
                            return Token.MOD;
                        }

                    case '~':
                        return Token.BITNOT;

                    case '+':
                        if (matchChar('='))
                        {
                            return Token.ASSIGN_ADD;
                        }
                        else if (matchChar('+'))
                        {
                            return Token.INC;
                        }
                        else
                        {
                            return Token.ADD;
                        }

                    case '-':
                        if (matchChar('='))
                        {
                            c = Token.ASSIGN_SUB;
                        }
                        else if (matchChar('-'))
                        {
                            if (!dirtyLine)
                            {
                               //html结尾
                                if (matchChar('>'))
                                {
                                    skipLine();
                                    goto retry;
                                }
                            }
                            c = Token.DEC;
                        }
                        else
                        {
                            c = Token.SUB;
                        }
                        dirtyLine = true;
                        return c;

                    default:
                        parser.addError("msg.illegal.character");
                        return Token.ERROR;
                }
            }

        }

        private void skipLine()
        {
            // 跳出行
            int c;
            while ((c = getChar()) != EOF_CHAR && c != '\n') { }
            ungetChar(c);
        }

        static bool isDigit(int c)
        {
            return '0' <= c && c <= '9';
        }

        #region
        private static readonly int
            Id_break = Token.BREAK,
            Id_case = Token.CASE,
            Id_continue = Token.CONTINUE,
            Id_default = Token.DEFAULT,
            Id_delete = Token.DELPROP,
            Id_do = Token.DO,
            Id_else = Token.ELSE,
            Id_export = Token.EXPORT,
            Id_false = Token.FALSE,
            Id_for = Token.FOR,
            Id_function = Token.FUNCTION,
            Id_if = Token.IF,
            Id_in = Token.IN,
            Id_let = Token.LET,
            Id_new = Token.NEW,
            Id_null = Token.NULL,
            Id_return = Token.RETURN,
            Id_switch = Token.SWITCH,
            Id_this = Token.THIS,
            Id_true = Token.TRUE,
            Id_typeof = Token.TYPEOF,
            Id_var = Token.VAR,
            Id_void = Token.VOID,
            Id_while = Token.WHILE,
            Id_with = Token.WITH,
            Id_yield = Token.YIELD,

            // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
            Id_abstract = Token.RESERVED,
            Id_boolean = Token.RESERVED,
            Id_byte = Token.RESERVED,
            Id_catch = Token.CATCH,
            Id_char = Token.RESERVED,
            Id_class = Token.RESERVED,
            Id_const = Token.CONST,
            Id_debugger = Token.DEBUGGER,
            Id_double = Token.RESERVED,
            Id_enum = Token.RESERVED,
            Id_extends = Token.RESERVED,
            Id_final = Token.RESERVED,
            Id_finally = Token.FINALLY,
            Id_float = Token.RESERVED,
            Id_goto = Token.RESERVED,
            Id_implements = Token.RESERVED,
            Id_import = Token.IMPORT,
            Id_instanceof = Token.INSTANCEOF,
            Id_int = Token.RESERVED,
            Id_interface = Token.RESERVED,
            Id_long = Token.RESERVED,
            Id_native = Token.RESERVED,
            Id_package = Token.RESERVED,
            Id_private = Token.RESERVED,
            Id_protected = Token.RESERVED,
            Id_public = Token.RESERVED,
            Id_short = Token.RESERVED,
            Id_static = Token.RESERVED,
            Id_super = Token.RESERVED,
            Id_synchronized = Token.RESERVED,
            Id_throw = Token.THROW,
            Id_throws = Token.RESERVED,
            Id_transient = Token.RESERVED,
            Id_try = Token.TRY,
            Id_volatile = Token.RESERVED,
            Id_undefined = Token.UNDEFINED;
        #endregion

        private static int stringToKeyword(String name)
        {

            int id;
            String s = name;
            //L0: {
            //    id = 0; String x = null; int c;
            //L: switch (s.Length)
            //    {

            //    }
            //}


            id = 0; String X = null; int c;
            switch (s.Length)
            {
                case 2: c = s.ToCharArray()[1];
                    if (c == 'f') { if (s.ToCharArray()[0] == 'i') { id = Id_if; goto L0; } }
                    else if (c == 'n') { if (s.ToCharArray()[0] == 'i') { id = Id_in; goto L0; } }
                    else if (c == 'o') { if (s.ToCharArray()[0] == 'd') { id = Id_do; goto L0; } }
                    goto L;
                case 3: switch (s.ToCharArray()[0])
                    {
                        case 'f': if (s.ToCharArray()[2] == 'r' && s.ToCharArray()[1] == 'o') { id = Id_for; goto L0; } goto L;
                        case 'i': if (s.ToCharArray()[2] == 't' && s.ToCharArray()[1] == 'n') { id = Id_int; goto L0; } goto L;
                        case 'l': if (s.ToCharArray()[2] == 't' && s.ToCharArray()[1] == 'e') { id = Id_let; goto L0; } goto L;
                        case 'n': if (s.ToCharArray()[2] == 'w' && s.ToCharArray()[1] == 'e') { id = Id_new; goto L0; } goto L;
                        case 't': if (s.ToCharArray()[2] == 'y' && s.ToCharArray()[1] == 'r') { id = Id_try; goto L0; } goto L;
                        case 'v': if (s.ToCharArray()[2] == 'r' && s.ToCharArray()[1] == 'a') { id = Id_var; goto L0; } goto L;
                    } goto L;
                case 4: switch (s.ToCharArray()[0])
                    {
                        case 'b': X = "byte"; id = Id_byte; goto L;
                        case 'c': c = s.ToCharArray()[3];
                            if (c == 'e') { if (s.ToCharArray()[2] == 's' && s.ToCharArray()[1] == 'a') { id = Id_case; goto L0; } }
                            else if (c == 'r') { if (s.ToCharArray()[2] == 'a' && s.ToCharArray()[1] == 'h') { id = Id_char; goto L0; } }
                            goto L;
                        case 'e': c = s.ToCharArray()[3];
                            if (c == 'e') { if (s.ToCharArray()[2] == 's' && s.ToCharArray()[1] == 'l') { id = Id_else; goto L0; } }
                            else if (c == 'm') { if (s.ToCharArray()[2] == 'u' && s.ToCharArray()[1] == 'n') { id = Id_enum; goto L0; } }
                            goto L;
                        case 'g': X = "goto"; id = Id_goto; goto L;
                        case 'l': X = "long"; id = Id_long; goto L;
                        case 'n': X = "null"; id = Id_null; goto L;
                        case 't': c = s.ToCharArray()[3];
                            if (c == 'e') { if (s.ToCharArray()[2] == 'u' && s.ToCharArray()[1] == 'r') { id = Id_true; goto L0; } }
                            else if (c == 's') { if (s.ToCharArray()[2] == 'i' && s.ToCharArray()[1] == 'h') { id = Id_this; goto L0; } }
                            goto L;
                        case 'v': X = "void"; id = Id_void; goto L;
                        case 'w': X = "with"; id = Id_with; goto L;
                    } goto L;
                case 5: switch (s.ToCharArray()[2])
                    {
                        case 'a': X = "class"; id = Id_class; goto L;
                        case 'e': c = s.ToCharArray()[0];
                            if (c == 'b') { X = "break"; id = Id_break; }
                            else if (c == 'y') { X = "yield"; id = Id_yield; }
                            goto L;
                        case 'i': X = "while"; id = Id_while; goto L;
                        case 'l': X = "false"; id = Id_false; goto L;
                        case 'n': c = s.ToCharArray()[0];
                            if (c == 'c') { X = "const"; id = Id_const; }
                            else if (c == 'f') { X = "final"; id = Id_final; }
                            goto L;
                        case 'o': c = s.ToCharArray()[0];
                            if (c == 'f') { X = "float"; id = Id_float; }
                            else if (c == 's') { X = "short"; id = Id_short; }
                            goto L;
                        case 'p': X = "super"; id = Id_super; goto L;
                        case 'r': X = "throw"; id = Id_throw; goto L;
                        case 't': X = "catch"; id = Id_catch; goto L;
                    } goto L;
                case 6: switch (s.ToCharArray()[1])
                    {
                        case 'a': X = "native"; id = Id_native; goto L;
                        case 'e': c = s.ToCharArray()[0];
                            if (c == 'd') { X = "delete"; id = Id_delete; }
                            else if (c == 'r') { X = "return"; id = Id_return; }
                            goto L;
                        case 'h': X = "throws"; id = Id_throws; goto L;
                        case 'm': X = "import"; id = Id_import; goto L;
                        case 'o': X = "double"; id = Id_double; goto L;
                        case 't': X = "static"; id = Id_static; goto L;
                        case 'u': X = "public"; id = Id_public; goto L;
                        case 'w': X = "switch"; id = Id_switch; goto L;
                        case 'x': X = "export"; id = Id_export; goto L;
                        case 'y': X = "typeof"; id = Id_typeof; goto L;
                    } goto L;
                case 7: switch (s.ToCharArray()[1])
                    {
                        case 'a': X = "package"; id = Id_package; goto L;
                        case 'e': X = "default"; id = Id_default; goto L;
                        case 'i': X = "finally"; id = Id_finally; goto L;
                        case 'o': X = "boolean"; id = Id_boolean; goto L;
                        case 'r': X = "private"; id = Id_private; goto L;
                        case 'x': X = "extends"; id = Id_extends; goto L;
                    } goto L;
                case 8: switch (s.ToCharArray()[0])
                    {
                        case 'a': X = "abstract"; id = Id_abstract; goto L;
                        case 'c': X = "continue"; id = Id_continue; goto L;
                        case 'd': X = "debugger"; id = Id_debugger; goto L;
                        case 'f': X = "function"; id = Id_function; goto L;
                        case 'v': X = "volatile"; id = Id_volatile; goto L;
                    } goto L;
                case 9: c = s.ToCharArray()[0];
                    if (c == 'i') { X = "interface"; id = Id_interface; }
                    else if (c == 'p') { X = "protected"; id = Id_protected; }
                    else if (c == 't') { X = "transient"; id = Id_transient; }
                    else if (c == 'u') { X = "undefined"; id = Id_undefined; }
                    goto L;
                case 10: c = s.ToCharArray()[1];
                    if (c == 'm') { X = "implements"; id = Id_implements; }
                    else if (c == 'n') { X = "instanceof"; id = Id_instanceof; }
                    goto L;
                case 12: X = "synchronized"; id = Id_synchronized; goto L;
            }
        L: ;
            if (X != null && X != s && !X.Equals(s)) id = 0;
        L0: ;
            if (id == 0) { return Token.EOF; }
            return id & 0xff;

        }
        private String getStringFromBuffer()
        {
            return new string(stringBuffer, 0, stringBufferTop);
        }
        private void addToString(int c)
        {
            int N = stringBufferTop;
            if (N == stringBuffer.Length)
            {
                char[] tmp = new char[stringBuffer.Length * 2];
                Array.Copy(stringBuffer, 0, tmp, 0, N);
                stringBuffer = tmp;
            }
            stringBuffer[N] = (char)c;
            stringBufferTop = N + 1;
        }
        private void ungetChar(int c)
        {
            // can not unread past across line boundary
            if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')
                Kit.codeBug();
            ungetBuffer[ungetCursor++] = c;
        }

        private int getChar()
        {
            if (ungetCursor != 0)
            {
                return ungetBuffer[--ungetCursor];
            }
            for (; ; )
            {
                int c;
                if (sourceString != null)
                {
                    if (sourceCursor == sourceEnd)
                    {
                        hitEOF = true;
                        return EOF_CHAR;
                    }
                    c = sourceString.ToCharArray()[sourceCursor++];
                }
                else
                {
                    if (sourceCursor == sourceEnd)
                    {
                        if (!fillSourceBuffer())
                        {
                            hitEOF = true;
                            return EOF_CHAR;
                        }
                    }
                    c = sourceBuffer[sourceCursor++];
                }
                if (lineEndChar >= 0)
                {
                    if (lineEndChar == '\r' && c == '\n')
                    {
                        lineEndChar = '\n';
                        continue;
                    }
                    lineEndChar = -1;
                    lineStart = sourceCursor - 1;
                    lineno++;
                }
                if (c <= 127)
                {
                    if (c == '\n' || c == '\r')
                    {
                        lineEndChar = c;
                        c = '\n';
                    }
                }
                else
                {
                    //如果是Js的 回车
                }
                return c;
            }

        }

        private bool fillSourceBuffer()
        {
            if (sourceString != null) Kit.codeBug();
            if (sourceEnd == sourceBuffer.Length)
            {
                if (lineStart != 0)
                {
                    Array.Copy(sourceBuffer, lineStart, sourceBuffer, 0,
                                     sourceEnd - lineStart);

                    sourceEnd -= lineStart;
                    sourceCursor -= lineStart;
                    lineStart = 0;
                }
                else
                {
                    char[] tmp = new char[sourceBuffer.Length * 2];
                    Array.Copy(sourceBuffer, 0, tmp, 0, sourceEnd);
                    // System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd);
                    sourceBuffer = tmp;
                }
            }
            int n = 0;
            //sourceReader.read(sourceBuffer, sourceEnd,
            //                      sourceBuffer.length - sourceEnd);
            if (n < 0)
            {
                return false;
            }
            sourceEnd += n;
            return true;
        }
/// <summary>
/// 是否是空格
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
       public static bool isJSSpace(int c)
        {
            if (c <= 127)
            {
                return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB;
            }
            else
            {
                return c == 0xA0;
            }
        }

        public int getOffset()
        {
            int n = sourceCursor - lineStart;
            if (lineEndChar >= 0) { --n; }
            return n;
        }

        public String getLine()
        {
            if (sourceString != null)
            {
                // String case
                int lineEnd = sourceCursor;
                if (lineEndChar >= 0)
                {
                    --lineEnd;
                }
                else
                {
                    for (; lineEnd != sourceEnd; ++lineEnd)
                    {
                        int c = sourceString.ToCharArray()[lineEnd];
                        if (ScriptRuntime.isJSLineTerminator(c))
                        {
                            break;
                        }
                    }
                }
                return sourceString.Substring(lineStart, lineEnd);
            }
            else
            {
                // Reader case
                int lineLength = sourceCursor - lineStart;
                if (lineEndChar >= 0)
                {
                    --lineLength;
                }
                else
                {
                    // Read until the end of line
                    for (; ; ++lineLength)
                    {
                        int i = lineStart + lineLength;
                        if (i == sourceEnd)
                        {
                            try
                            {
                                if (!fillSourceBuffer()) { break; }
                            }
                            catch (IOException ioe)
                            {
                                // ignore it, we're already displaying an error...
                                break;
                            }
                            // i recalculuation as fillSourceBuffer can move saved
                            // line buffer and change lineStart
                            i = lineStart + lineLength;
                        }
                        int c = sourceBuffer[i];
                        if (ScriptRuntime.isJSLineTerminator(c))
                        {
                            break;
                        }
                    }
                }
                return new String(sourceBuffer, lineStart, lineLength);
            }
        }

        // stuff other than whitespace since start of line
        private bool dirtyLine;

        public int getFirstXMLToken()
        {
            xmlOpenTagsCount = 0;
            xmlIsAttribute = false;
            xmlIsTagContent = false;
            ungetChar('<');
            return getNextXMLToken();
        }

        private int peekChar()
        {
            int c = getChar();
            ungetChar(c);
            return c;
        }
        private bool readQuotedString(int quote)
        {
            for (int c = getChar(); c != EOF_CHAR; c = getChar())
            {
                addToString(c);
                if (c == quote) return true;
            }

            stringBufferTop = 0; // throw away the string in progress
            this._string = null;
            parser.addError("msg.XML.bad.form");
            return false;
        }

        private bool readXmlComment()
        {
            for (int c = getChar(); c != EOF_CHAR; )
            {
                addToString(c);
                if (c == '-' && peekChar() == '-')
                {
                    c = getChar();
                    addToString(c);
                    if (peekChar() == '>')
                    {
                        c = getChar(); // Skip >
                        addToString(c);
                        return true;
                    }
                    else
                    {
                        continue;
                    }
                }
                c = getChar();
            }

            stringBufferTop = 0; // throw away the string in progress
            this._string = null;
            parser.addError("msg.XML.bad.form");
            return false;
        }

        private bool readCDATA()
        {
            for (int c = getChar(); c != EOF_CHAR; )
            {
                addToString(c);
                if (c == ']' && peekChar() == ']')
                {
                    c = getChar();
                    addToString(c);
                    if (peekChar() == '>')
                    {
                        c = getChar(); // Skip >
                        addToString(c);
                        return true;
                    }
                    else
                    {
                        continue;
                    }
                }
                c = getChar();
            }

            stringBufferTop = 0; // throw away the string in progress
            this._string = null;
            parser.addError("msg.XML.bad.form");
            return false;
        }

        /**
         *
         */
        private bool readEntity()
        {
            int declTags = 1;
            for (int c = getChar(); c != EOF_CHAR; c = getChar())
            {
                addToString(c);
                switch (c)
                {
                    case '<':
                        declTags++;
                        break;
                    case '>':
                        declTags--;
                        if (declTags == 0) return true;
                        break;
                }
            }

            stringBufferTop = 0; // throw away the string in progress
            this._string = null;
            parser.addError("msg.XML.bad.form");
            return false;
        }

        /**
         *
         */
        private bool readPI()
        {
            for (int c = getChar(); c != EOF_CHAR; c = getChar())
            {
                addToString(c);
                if (c == '?' && peekChar() == '>')
                {
                    c = getChar(); // Skip >
                    addToString(c);
                    return true;
                }
            }

            stringBufferTop = 0; // throw away the string in progress
            this._string = null;
            parser.addError("msg.XML.bad.form");
            return false;
        }

        public bool isXMLAttribute()
        {
            return xmlIsAttribute;
        }

        public int getNextXMLToken()
        {
            stringBufferTop = 0; // remember the XML

            for (int c = getChar(); c != EOF_CHAR; c = getChar())
            {
                if (xmlIsTagContent)
                {
                    switch (c)
                    {
                        case '>':
                            addToString(c);
                            xmlIsTagContent = false;
                            xmlIsAttribute = false;
                            break;
                        case '/':
                            addToString(c);
                            if (peekChar() == '>')
                            {
                                c = getChar();
                                addToString(c);
                                xmlIsTagContent = false;
                                xmlOpenTagsCount--;
                            }
                            break;
                        case '{':
                            ungetChar(c);
                            this._string = getStringFromBuffer();
                            return Token.XML;
                        case '\'':
                        case '"':
                            addToString(c);
                            if (!readQuotedString(c)) return Token.ERROR;
                            break;
                        case '=':
                            addToString(c);
                            xmlIsAttribute = true;
                            break;
                        case ' ':
                        case '\t':
                        case '\r':
                        case '\n':
                            addToString(c);
                            break;
                        default:
                            addToString(c);
                            xmlIsAttribute = false;
                            break;
                    }

                    if (!xmlIsTagContent && xmlOpenTagsCount == 0)
                    {
                        this._string = getStringFromBuffer();
                        return Token.XMLEND;
                    }
                }
                else
                {
                    switch (c)
                    {
                        case '<':
                            addToString(c);
                            c = peekChar();
                            switch (c)
                            {
                                case '!':
                                    c = getChar(); // Skip !
                                    addToString(c);
                                    c = peekChar();
                                    switch (c)
                                    {
                                        case '-':
                                            c = getChar(); // Skip -
                                            addToString(c);
                                            c = getChar();
                                            if (c == '-')
                                            {
                                                addToString(c);
                                                if (!readXmlComment()) return Token.ERROR;
                                            }
                                            else
                                            {
                                                // throw away the string in progress
                                                stringBufferTop = 0;
                                                this._string = null;
                                                parser.addError("msg.XML.bad.form");
                                                return Token.ERROR;
                                            }
                                            break;
                                        case '[':
                                            c = getChar(); // Skip [
                                            addToString(c);
                                            if (getChar() == 'C' &&
                                                getChar() == 'D' &&
                                                getChar() == 'A' &&
                                                getChar() == 'T' &&
                                                getChar() == 'A' &&
                                                getChar() == '[')
                                            {
                                                addToString('C');
                                                addToString('D');
                                                addToString('A');
                                                addToString('T');
                                                addToString('A');
                                                addToString('[');
                                                if (!readCDATA()) return Token.ERROR;

                                            }
                                            else
                                            {
                                                // throw away the string in progress
                                                stringBufferTop = 0;
                                                this._string = null;
                                                parser.addError("msg.XML.bad.form");
                                                return Token.ERROR;
                                            }
                                            break;
                                        default:
                                            if (!readEntity()) return Token.ERROR;
                                            break;
                                    }
                                    break;
                                case '?':
                                    c = getChar(); // Skip ?
                                    addToString(c);
                                    if (!readPI()) return Token.ERROR;
                                    break;
                                case '/':
                                    // End tag
                                    c = getChar(); // Skip /
                                    addToString(c);
                                    if (xmlOpenTagsCount == 0)
                                    {
                                        // throw away the string in progress
                                        stringBufferTop = 0;
                                        this._string = null;
                                        parser.addError("msg.XML.bad.form");
                                        return Token.ERROR;
                                    }
                                    xmlIsTagContent = true;
                                    xmlOpenTagsCount--;
                                    break;
                                default:
                                    // Start tag
                                    xmlIsTagContent = true;
                                    xmlOpenTagsCount++;
                                    break;
                            }
                            break;
                        case '{':
                            ungetChar(c);
                            this._string = getStringFromBuffer();
                            return Token.XML;
                        default:
                            addToString(c);
                            break;
                    }
                }
            }

            stringBufferTop = 0; // throw away the string in progress
            this._string = null;
            parser.addError("msg.XML.bad.form");
            return Token.ERROR;
        }


        /**
     * Parser calls the method when it gets / or /= in literal context.
     */
        public void readRegExp(int startToken)
        {
            stringBufferTop = 0;
            if (startToken == Token.ASSIGN_DIV)
            {
                // Miss-scanned /=
                addToString('=');
            }
            else
            {
                if (startToken != Token.DIV) Kit.codeBug();
            }

            Boolean inCharSet = false; // true if inside a '['..']' pair
            int c;
            while ((c = getChar()) != '/' || inCharSet)
            {
                if (c == '\n' || c == EOF_CHAR)
                {
                    ungetChar(c);
                    throw parser.reportError("msg.unterminated.re.lit");
                }
                if (c == '\\')
                {
                    addToString(c);
                    c = getChar();
                }
                else if (c == '[')
                {
                    inCharSet = true;
                }
                else if (c == ']')
                {
                    inCharSet = false;
                }
                addToString(c);
            }
            int reEnd = stringBufferTop;

            while (true)
            {
                if (matchChar('g'))
                    addToString('g');
                else if (matchChar('i'))
                    addToString('i');
                else if (matchChar('m'))
                    addToString('m');
                else
                    break;
            }

            if (isAlpha(peekChar()))
            {
                throw parser.reportError("msg.invalid.re.flag");
            }

            this._string = new String(stringBuffer, 0, reEnd);
            this.regExpFlags = new String(stringBuffer, reEnd,
                                          stringBufferTop - reEnd);
        }

        private void ungetCharIgnoreLineEnd(int c)
        {
            ungetBuffer[ungetCursor++] = c;
        }

        private static bool isAlpha(int c)
        {
            // Use 'Z' < 'a'
            if (c <= 'Z')
            {
                return 'A' <= c;
            }
            else
            {
                return 'a' <= c && c <= 'z';
            }
        }
        private bool matchChar(int test)
        {
            int c = getCharIgnoreLineEnd();
            if (c == test)
            {
                return true;
            }
            else
            {
                ungetCharIgnoreLineEnd(c);
                return false;
            }
        }

        private int getCharIgnoreLineEnd()
        {
            if (ungetCursor != 0)
            {
                return ungetBuffer[--ungetCursor];
            }

            for (; ; )
            {
                int c;
                if (sourceString != null)
                {
                    if (sourceCursor == sourceEnd)
                    {
                        hitEOF = true;
                        return EOF_CHAR;
                    }
                    c = sourceString.ToCharArray()[sourceCursor++];
                }
                else
                {
                    if (sourceCursor == sourceEnd)
                    {
                        if (!fillSourceBuffer())
                        {
                            hitEOF = true;
                            return EOF_CHAR;
                        }
                    }
                    c = sourceBuffer[sourceCursor++];
                }

                if (c <= 127)
                {
                    if (c == '\n' || c == '\r')
                    {
                        lineEndChar = c;
                        c = '\n';
                    }
                }
                else
                {
                    if (isJSFormatChar(c))
                    {
                        continue;
                    }
                    if (ScriptRuntime.isJSLineTerminator(c))
                    {
                        lineEndChar = c;
                        c = '\n';
                    }
                }
                return c;
            }
        }
        private static bool isJSFormatChar(int c)
        {
            // return c > 127 && Character.getType((char)c) == Character.FORMAT;
            return c > 127;
        }

        public static bool isKeyword(String s)
        {
            return Token.EOF != stringToKeyword(s);
        }

        /*
    * For chars - because we need something out-of-range
    * to check.  (And checking EOF by exception is annoying.)
    * Note distinction from EOF token type!
    */
        private static int
            EOF_CHAR = -1;

        private int lineStart = 0;
        private int lineno;
        private int lineEndChar = -1;

        private char[] stringBuffer = new char[128];
        private int stringBufferTop;
        private ObjToIntMap allStrings = new ObjToIntMap(50);

        // Room to backtrace from to < on failed match of the last - in <!--
        private int[] ungetBuffer = new int[3];

        private int ungetCursor;

        // Set this to an initial non-null value so that the Parser has
        // something to retrieve even if an error has occurred and no
        // string is found.  Fosters one class of error, but saves lots of
        // code.
        private String _string = "";
        private double _number;

        private bool hitEOF = false;
        private Parser parser = null;

        public String regExpFlags;

        private StreamReader sourceReader;
        private char[] sourceBuffer;
        private String sourceString = null;
        /// <summary>
        /// 源代码结束位置
        /// </summary>
        private int sourceEnd = 0;
        private int sourceCursor = 0;

        // for xml tokenizer
        private bool xmlIsAttribute;
        private bool xmlIsTagContent;
        private int xmlOpenTagsCount;
    }
}
